#include <stdio.h>
#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include "public.h"
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include "ether_frame.h"
#include "tcp_conversitons.h"

char *g_program_name = "mytcpdump";

struct options g_opts;
TCPConversiations g_tcpconvs;

static const struct option longopts[] = {
{"interface", required_argument, NULL, 'i'},
{"tcp-dump-path", required_argument, NULL, 'o'},
{NULL, 0, NULL, 0}
};

#define SHORTOPTS "i:r:o:"
void print_usage()
{
    printf("%s usage:\n", g_program_name);
    printf("\t [-i interface]\n");
    printf("\t [-r filename]\n");
    printf("\t [-o tcp data dump path]\n");
    printf("\t [expression]\n");
}

void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
    //class EtherFrame *frame = NULL;
    printf("Packet length: %d\n", pkthdr->len);
    printf("Number of bytes: %d\n", pkthdr->caplen);
    printf("Recieved time: %s\n", ctime((const time_t *)&pkthdr->ts.tv_sec));
    if(g_opts.frametype == DLT_EN10MB)
    {
        EtherFrame frame(pkthdr, packet);
        frame.dissection();
    }
    else
    {
        printf("frame type %d not implemented\n", g_opts.frametype);
    }
}

int mytcpdump_doit()
{
    struct bpf_program filter; /*已经编译好的过滤表达式*/
    const char *devices = NULL;
    char ebuf[PCAP_ERRBUF_SIZE];
    pcap_t *pd = NULL;
    int ret = -1;

    if(g_opts.file_pcap_in != NULL)
    {
        pd = pcap_open_offline(g_opts.file_pcap_in, ebuf);
    }
    else
    {
        if (g_opts.interface == NULL)
        {
            devices = pcap_lookupdev(ebuf);
            if (devices == NULL){
                error("%s\n", ebuf);
                return -1;
            }
        }
        else
        {
            devices = g_opts.interface;
        }

        pd = pcap_open_live(devices, 65535, 1, 0, ebuf);
    }

    if (pd == NULL)
        error("%s", ebuf);

    bpf_u_int32 mask; /* 所在网络的掩码 */
    bpf_u_int32 net; /* 主机的IP地址 */
    /* 探查设备属性 */
    pcap_lookupnet(devices, &net, &mask, ebuf);

    ret = pcap_compile(pd, &filter, g_opts.expression, 0, net);
    if(ret == -1){
        error("filter expression error\n");
    }
    ret = pcap_setfilter(pd, &filter);

    g_opts.frametype = pcap_datalink(pd);

    pcap_loop(pd, -1, getPacket, NULL);

    pcap_close(pd);
    return 0;
}

int mytcpdump_getarg(int argc, char *argv[])
{
    register int op;
    while ((op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1)
    {
        switch (op) {
        case 'i':
            g_opts.interface = optarg;
            break;
        case 'r':
            g_opts.file_pcap_in = optarg;
            break;
        case 'o':
            g_opts.file_tcp_dump = optarg;
            break;
        default:
            print_usage();
            exit(1);
        }
    }

    g_opts.expression = copy_argv(&argv[optind]);

    return 0;
}

int main(int argc, char *argv[])
{
    memset(&g_opts, 0, sizeof(g_opts));

    mytcpdump_getarg(argc, argv);
    mytcpdump_doit();
    while(1)
        sleep(5);
    return 0;
}

